package com.tanx.cqrs.infrastructure.spring.command.handler;

import com.tanx.cqrs.command.Command;
import com.tanx.cqrs.command.handler.CommandHandler;
import com.tanx.cqrs.command.handler.CommandHandlerInstance;
import com.tanx.cqrs.command.handler.CommandHandlerResolver;
import com.tanx.cqrs.eventsourcing.EventSourcingMata;
import com.tanx.cqrs.eventsourcing.EventSourcingRepository;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * spring bean的命令解析器
 */
@Slf4j
public class SpringBeanCommandHandlerResolverImpl implements CommandHandlerResolver {

    private Map<Class<? extends Command>, CommandHandlerInstance> instanceMap = new ConcurrentHashMap<>();
    private EventSourcingRepository eventSourcingRepository;
    private ApplicationContext context;

    public SpringBeanCommandHandlerResolverImpl(EventSourcingRepository eventSourcingRepository, ApplicationContext context) {
        this.eventSourcingRepository = eventSourcingRepository;
        this.context = context;
    }

    @Override
    public CommandHandlerInstance resolve(Command command) {
        CommandHandlerInstance instance = instanceMap.get(command.getClass());
        if (instance != null) {
            return instance;
        }
        throw new RuntimeException("command " + command.getClass().getSimpleName() + " handler not found ");
    }

    /**
     * 调用命令处理器
     * 1,查询到命令处理器
     * 2,事件溯源-让聚合保持最新状态
     * 2.1,获取溯源列表(第一个为自己,如果自己为聚合)
     * 2.2,先查询是否有快照
     * 2.3,如果存在快照,先以快照为基础,然后加载快照产生后发生的事件
     * 2.4,执行命令
     * 3,执行命令处理器
     * //     * 4,发布事件到EventBus
     *
     * @param command 命令
     * @return 执行结果
     */
    @Override
    public Object invoke(@NonNull Command command) {
        CommandHandlerInstance resolve = resolve(command);
        List<EventSourcingMata> matas = resolve.getEventSourcingMetas(command);
        log.debug("溯源列表:" + matas.toString());
        //溯源
        List<Object> aggregates = eventSourcingRepository.eventSourcingList(matas);
        //调用命令处理器
        return resolve.invoke(command, aggregates.toArray());
    }

    /**
     * 1,遍历所有的bean,得到注解有@CommandHandler的bean
     * 2,生成CommandHandlerInstance
     * 3,加入列表中
     */
    @Override
    public void registerAllHandler() {
        Map<String, Object> beansOfType = context.getBeansOfType(Object.class);
        for (Object bean : beansOfType.values()) {
            Class<?> beanClass = bean.getClass();
            CommandHandler classHandler = beanClass.getAnnotation(CommandHandler.class);
            //如果是类上存在注解,那么此类都作为命令处理器,其中的所有方法都作为处理器
            if (classHandler != null) {
                Method[] methods = beanClass.getMethods();
                addInstances(bean, methods);
                continue;
            }
            Method[] methods = bean.getClass().getMethods();
            for (Method method : methods) {
                CommandHandler annotation = method.getAnnotation(CommandHandler.class);
                if (annotation != null) {
                    addInstances(bean, method);
                }
            }
        }

    }

    private void addInstances(Object bean, Method... methods) {
        for (Method method : methods) {
            SpringBeanCommandHandlerInstance instance = new SpringBeanCommandHandlerInstance(bean, method);
            instanceMap.put(instance.getCommandClass(), instance);
        }
    }
}
